/**
 *
 * Kalle Dalheimer <kalle@kde.org>
 */


#include <tqfile.h>
#include <tqpainter.h>
#include <tqcursor.h>
#include <tqpopupmenu.h>
#include <tqpaintdevicemetrics.h>
#include <tqcstring.h> // For TQByteArray

#include <tdeaction.h>
#include <tdeglobal.h>
#include <tdelocale.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <kprinter.h>
#include <kstandarddirs.h>
#include <tdetempfile.h>
#include <dcopobject.h>
#include <kxmlguifactory.h>
#include <tdefiledialog.h>
#include <tdemessagebox.h>

#include <KoTemplateCreateDia.h>

#include "kdchart/KDChart.h"
#include "kchart_view.h"
#include "kchart_factory.h"
#include "kchart_part.h"
#include "kchart_params.h"
#include "kchartWizard.h"
#include "kchartDataEditor.h"
#include "kchartConfigDialog.h"
#include "KChartViewIface.h"
#include "kchartPageLayout.h"
#include "kchartPrinterDlg.h"
#include "csvimportdialog.h"


using namespace std;


//#include "sheetdlg.h"

namespace KChart
{


KChartView::KChartView( KChartPart* part, TQWidget* parent, const char* name )
    : KoView( part, parent, name )
{

    // No flicker
    setBackgroundMode( TQt::NoBackground );

    setInstance( KChartFactory::global() );
    if ( koDocument()->isReadWrite() )
        setXMLFile( "kchart.rc" );
    else
        setXMLFile( "kchart_readonly.rc" );
    m_dcop = 0;
    dcopObject(); // build it

    m_importData = new TDEAction( i18n( "Import Data..." ), 0,
				this, TQ_SLOT( importData() ),
				actionCollection(), "import_data" );
#if 0
    TDEAction * actionExtraCreateTemplate	=
#endif
	new TDEAction( i18n( "&Create Template From Document..." ), 0,
		       this, TQ_SLOT( extraCreateTemplate() ),
		       actionCollection(), "extra_template" );

    m_wizard = new TDEAction( i18n("Customize with &Wizard..."),
                            "wizard", 0,
                            this, TQ_SLOT( wizard() ),
                            actionCollection(), "wizard");
    m_edit = new TDEAction( i18n("Edit &Data..."), "edit", 0,
                          this, TQ_SLOT( editData() ),
                          actionCollection(), "editdata");
    m_config = new TDEAction( i18n( "&Chart..." ), "configure", 0,

                            this, TQ_SLOT( slotConfig() ),
                            actionCollection(), "config" );

    // One TDEToggleAction per chart type
    m_chartbars = new TDEToggleAction( i18n("&Bar"), "chart_bar_3d", 0, this,
                                     TQ_SLOT( barsChart() ), actionCollection(),
                                     "barschart");
    m_chartbars->setExclusiveGroup( "charttypes" );
    m_chartbars->setChecked( true );

    m_chartline = new TDEToggleAction( i18n("&Line"), "chart_line", 0, this,
                                     TQ_SLOT( lineChart() ), actionCollection(),
                                     "linechart");
    m_chartline->setExclusiveGroup( "charttypes" );

    m_chartareas = new TDEToggleAction( i18n("&Area"), "chart_area", 0, this,
                                      TQ_SLOT( areasChart() ), actionCollection(),
                                      "areaschart");
    m_chartareas->setExclusiveGroup( "charttypes" );

    m_charthilo = new TDEToggleAction( i18n("&HiLo"), "chart_hilo", 0, this,
                                     TQ_SLOT( hiLoChart() ), actionCollection(),
                                     "hilochart");
    m_charthilo->setExclusiveGroup( "charttypes" );

    m_chartbw = new TDEToggleAction( i18n("Bo&x && Whiskers"), "chart_boxwhisker", 0, this,
                                     TQ_SLOT( bwChart() ), actionCollection(),
                                     "bwchart");
    m_chartbw->setExclusiveGroup( "charttypes" );

    m_chartpie = new TDEToggleAction( i18n("&Pie"), "chart_pie", 0, this,
                                    TQ_SLOT( pieChart() ), actionCollection(),
                                    "piechart");
    m_chartpie->setExclusiveGroup( "charttypes" );

    m_chartring = new TDEToggleAction( i18n("&Ring"), "chart_ring", 0, this,
                                     TQ_SLOT( ringChart() ), actionCollection(),
                                     "ringchart");
    m_chartring->setExclusiveGroup( "charttypes" );

    m_chartpolar = new TDEToggleAction( i18n("&Polar"), "chart_polar", 0, this,
                                     TQ_SLOT( polarChart() ), actionCollection(),
                                     "polarchart");
    m_chartpolar->setExclusiveGroup( "charttypes" );

    // Configuration TDEActions
    m_colorConfig = new TDEAction( i18n( "&Colors..." ), 0,
                            this, TQ_SLOT( slotConfigColor() ),
                            actionCollection(), "color_config" );

    m_fontConfig = new TDEAction( i18n( "&Font..." ), 0,
                            this, TQ_SLOT( slotConfigFont() ),
                            actionCollection(), "font_config" );

    m_backConfig = new TDEAction( i18n( "&Background..." ), 0,
                            this, TQ_SLOT( slotConfigBack() ),
                            actionCollection(), "back_config" );

    m_legendConfig = new TDEAction( i18n( "&Legend..." ), 0,
                            this, TQ_SLOT( slotConfigLegend() ),
                            actionCollection(), "legend_config" );

    m_subTypeChartConfig = new TDEAction( i18n( "Chart &Sub-type..." ), 0,
                            this, TQ_SLOT( slotConfigSubTypeChart() ),
                            actionCollection(), "legend_subtype" );

    m_dataFormatConfig = new TDEAction( i18n( "&Data Format..." ), 0,
                            this, TQ_SLOT( slotConfigDataFormat() ),
                            actionCollection(), "data_format" );

    m_headerFooterConfig = new TDEAction( i18n( "&Header && Footer..." ), 0,
                            this, TQ_SLOT( slotConfigHeaderFooterChart() ),
                            actionCollection(), "headerfooter_subtype" );

    m_pageLayoutConfig = new TDEAction( i18n( "Page Layout..." ), 0,
                            this, TQ_SLOT( slotConfigPageLayout() ),
                            actionCollection(), "page_layout" );

    // initialize the configuration
    //    loadConfig();

    // Disable some things if we can't change the data, e.g. because
    // we are inside another application that provides the data for us.
    if (!((KChartPart*)koDocument())->canChangeValue()) {
	m_edit->setEnabled(false);
	m_importData->setEnabled(false);
    }

    updateGuiTypeOfChart();
}


KChartView::~KChartView()
{
    delete m_dcop;
}


DCOPObject* KChartView::dcopObject()
{
    if ( !m_dcop )
	m_dcop = new KChartViewIface( this );

    return m_dcop;
}


void KChartView::paintEvent( TQPaintEvent* /*ev*/ )
{
    TQPainter painter( this );

    // ### TODO: Scaling

    // Let the document do the drawing
    // This calls KChartPart::paintContent, basically.
    koDocument()->paintEverything( painter, rect(), false, this );
}


void KChartView::updateReadWrite( bool /*readwrite*/ )
{
#ifdef __GNUC__
#warning TODO
#endif
}


// Edit the data to the chart.
// This opens a spreadsheet like editor with the data in it.
//

void KChartView::editData()
{
    kchartDataEditor   ed(this);
    KChartParams      *params = ((KChartPart*)koDocument())->params();
    KDChartTableData  *dat    = ((KChartPart*)koDocument())->data();

    kdDebug(35001) << "***Before calling editor: cols =" << dat->cols()
		   << " , rows = "     << dat->rows()
		   << " , usedCols = " << dat->usedCols()
		   << "  usedRows = "  << dat->usedRows() << endl;

    ed.setData( params, dat );
    ed.setRowLabels(((KChartPart*)koDocument())->rowLabelTexts());
    ed.setColLabels(((KChartPart*)koDocument())->colLabelTexts());

    // Activate the Apply button in the editor.
    connect(&ed,  TQ_SIGNAL(applyClicked(kchartDataEditor *)),
	    this, TQ_SLOT(applyEdit(kchartDataEditor *)));

    // Execute the data editor.
    if ( ed.exec() != TQDialog::Accepted ) {
        return;
    }
    if (!ed.modified())
	return;

    // Get the data and legend back.
    ed.getData(params, dat);
    ed.getRowLabels(((KChartPart*)koDocument())->rowLabelTexts());
    ed.getColLabels(((KChartPart*)koDocument())->colLabelTexts());
    ((KChartPart*)koDocument())->setModified(true);

    kdDebug(35001) << "***After calling editor: cols =" << dat->cols()
		   << " , rows = "     << dat->rows()
		   << " , usedCols = " << dat->usedCols()
		   << "  usedRows = "  << dat->usedRows() << endl;
    update();
}


void KChartView::applyEdit(kchartDataEditor *ed)
{
    if (!ed->modified())
	return;

    ed->getData( ((KChartPart*)koDocument())->params(), 
		 ((KChartPart*)koDocument())->data() );
    ed->getRowLabels(((KChartPart*)koDocument())->rowLabelTexts());
    ed->getColLabels(((KChartPart*)koDocument())->colLabelTexts());

    ((KChartPart*)koDocument())->setModified(true);

    update();
}


void KChartView::wizard()
{
    kdDebug(35001) << "Wizard called" << endl;
    KChartWizard *wiz = new KChartWizard((KChartPart*)koDocument(), this,
					 "KChart Wizard", true);
    kdDebug(35001) << "Executed. Now, display it" << endl;
    if (wiz->exec()) {
	((KChartPart*)koDocument())->setModified(true);
        update();
        updateGuiTypeOfChart();
        kdDebug(35001) << "Ok, executed..." << endl;
    }
}


void KChartView::updateGuiTypeOfChart()
{
    KDChartParams* params = ((KChartPart*)koDocument())->params();

    switch(params->chartType()) {
    case KDChartParams::Bar:
	m_chartbars->setChecked(true);
	break;
    case KDChartParams::Line:
	m_chartline->setChecked(true);
	break;
    case KDChartParams::Area:
	m_chartareas->setChecked(true);
	break;
    case KDChartParams::Pie:
	m_chartpie->setChecked(true);
	break;
    case KDChartParams::HiLo:
	m_charthilo->setChecked(true);
	break;
    case KDChartParams::Ring:
	m_chartring->setChecked(true);
	break;
    case KDChartParams::Polar:
        m_chartpolar->setChecked(true);
        break;
    case KDChartParams::BoxWhisker:
        m_chartbw->setChecked( true );
        break;
    default:
	//todo
	break;
    }

    // Disable subtype configuration button if appropriate.
    updateButton();
}


void KChartView::slotConfig()
{
    config(KChartConfigDialog::KC_ALL);
}


void KChartView::config(int flags)
{
    // open a config dialog depending on the chart type
    KChartParams        *params = ((KChartPart*)koDocument())->params();
    KDChartTableData    *dat    = ((KChartPart*)koDocument())->data();

    KChartConfigDialog  *d      = new KChartConfigDialog( params, this, flags,
							  dat );

    connect( d, TQ_SIGNAL( dataChanged() ),
             this, TQ_SLOT( slotRepaint() ) );
    d->exec();
    delete d;
}


void KChartView::slotRepaint()
{
    ((KChartPart*)koDocument())->setModified(true);
    update();
}


void KChartView::saveConfig()
{
    kdDebug(35001) << "Save config..." << endl;
    ((KChartPart*)koDocument())->saveConfig( TDEGlobal::config() );
}


void KChartView::loadConfig()
{
    kdDebug(35001) << "Load config..." << endl;

    TDEGlobal::config()->reparseConfiguration();
    ((KChartPart*)koDocument())->loadConfig( TDEGlobal::config() );

    updateGuiTypeOfChart();
    //refresh chart when you load config
    update();
}


void KChartView::defaultConfig()
{
    ((KChartPart*)koDocument())->defaultConfig(  );
    updateGuiTypeOfChart();
    update();
}


void KChartView::pieChart()
{
    if ( m_chartpie->isChecked() ) {
	forceAxisParams(false);
	KChartParams  *params = ((KChartPart*)koDocument())->params();

	params->setChartType( KChartParams::Pie );
	params->setThreeDPies(params->threeDBars());
	params->setExplodeFactor( 0 );
	params->setExplode( true );

	updateButton();
	update();
	((KChartPart*)koDocument())->setModified(true);
    }
    else
        m_chartpie->setChecked( true ); // always one has to be checked !
}

void KChartView::forceAxisParams(bool lineMode) {
    KChartParams  *params = ((KChartPart*)koDocument())->params();
    KDChartAxisParams  axisParams;
    axisParams = params->axisParams( KDChartAxisParams::AxisPosLeft );
    if(params->chartType() == KChartParams::Line)
        m_logarithmicScale = axisParams.axisCalcMode();
    if(lineMode) {
        if(m_logarithmicScale)
            axisParams.setAxisCalcMode(KDChartAxisParams::AxisCalcLogarithmic);
    } else
        axisParams.setAxisCalcMode(KDChartAxisParams::AxisCalcLinear);
    params->setAxisParams( KDChartAxisParams::AxisPosLeft, axisParams );
}

void KChartView::lineChart()
{
    if ( m_chartline->isChecked() ) {
	forceAxisParams(true);
	KChartParams* params = ((KChartPart*)koDocument())->params();

	params->setChartType( KChartParams::Line );
	params->setLineChartSubType( KDChartParams::LineNormal );

	updateButton();
	update();
	((KChartPart*)koDocument())->setModified(true);
    }
    else
	m_chartline->setChecked( true ); // always one has to be checked !

}


void KChartView::barsChart()
{
    if ( m_chartbars->isChecked() ) {
	forceAxisParams(false);
	KChartParams* params = ((KChartPart*)koDocument())->params();

	params->setChartType( KChartParams::Bar );
	params->setBarChartSubType( KDChartParams::BarNormal );

	updateButton();
    params->setThreeDBars( params->threeDPies() );
	update();
	((KChartPart*)koDocument())->setModified(true);
    }
    else
	m_chartbars->setChecked( true ); // always one has to be checked !
}


void KChartView::areasChart()
{
    if ( m_chartareas->isChecked() ) {
	forceAxisParams(false);
	KChartParams* params = ((KChartPart*)koDocument())->params();

	params->setChartType( KChartParams::Area );
	params->setAreaChartSubType( KDChartParams::AreaNormal );

	updateButton();
	update();
	((KChartPart*)koDocument())->setModified(true);
    }
    else
	m_chartareas->setChecked( true ); // always one has to be checked !

}


void KChartView::hiLoChart()
{
    if ( m_charthilo->isChecked() ) {
	forceAxisParams(false);
	KChartParams* params = ((KChartPart*)koDocument())->params();

	params->setChartType( KChartParams::HiLo );
	params->setHiLoChartSubType( KDChartParams::HiLoNormal );

	updateButton();
	update();
	((KChartPart*)koDocument())->setModified(true);
    }
    else
	m_charthilo->setChecked( true ); // always one has to be checked !
}


void KChartView::ringChart()
{
    if ( m_chartring->isChecked() ) {
	forceAxisParams(false);
	KChartParams* params = ((KChartPart*)koDocument())->params();

	params->setChartType( KChartParams::Ring );

	updateButton();
	update();
	((KChartPart*)koDocument())->setModified(true);
    }
    else
	m_chartring->setChecked( true ); // always one has to be checked !

}


void KChartView::polarChart()
{
    if ( m_chartpolar->isChecked() ) {
	forceAxisParams(false);
        KDChartParams* params = ((KChartPart*)koDocument())->params();

        params->setChartType( KDChartParams::Polar );
        params->setPolarChartSubType( KDChartParams::PolarNormal );

        update();
	((KChartPart*)koDocument())->setModified(true);
    }
    else
        m_chartpolar->setChecked( true ); // always one has to be checked !
}


void KChartView::bwChart()
{
    if ( m_chartbw->isChecked() ) {
	forceAxisParams(false);
        KDChartParams* params = ((KChartPart*)koDocument())->params();

        params->setChartType( KDChartParams::BoxWhisker );
        params->setBWChartSubType( KDChartParams::BWNormal );

        update();
	((KChartPart*)koDocument())->setModified(true);
    }
    else
        m_chartbw->setChecked( true ); // always one has to be checked !
}


void KChartView::mousePressEvent ( TQMouseEvent *e )
{
    if (!koDocument()->isReadWrite() || !factory())
        return;
    if ( e->button() == TQt::RightButton )
        ((TQPopupMenu*)factory()->container("action_popup",this))->popup(TQCursor::pos());
}


void KChartView::slotConfigColor()
{
    config(KChartConfigDialog::KC_COLORS);
}


void KChartView::slotConfigFont()
{
    config(KChartConfigDialog::KC_FONT);
}


void KChartView::slotConfigBack()
{
    config(KChartConfigDialog::KC_BACK);
}


void KChartView::slotConfigLegend()
{
   config(KChartConfigDialog::KC_LEGEND);
}

void KChartView::slotConfigDataFormat()
{
    config(KChartConfigDialog::KC_DATAFORMAT);
}

void KChartView::slotConfigSubTypeChart()
{
    config(KChartConfigDialog::KC_SUBTYPE);
}


void KChartView::slotConfigHeaderFooterChart()
{
    config(KChartConfigDialog::KC_HEADERFOOTER);
}


// FIXME: Rename into something suitable.
void KChartView::updateButton()
{
    // Disable sub chart config item.
    KChartParams* params = ((KChartPart*)koDocument())->params();

    bool state=(params->chartType()==KChartParams::Bar ||
                params->chartType()==KChartParams::Area ||
                params->chartType()==KChartParams::Line ||
                params->chartType()==KChartParams::HiLo ||
                params->chartType()==KChartParams::Polar);
    m_subTypeChartConfig->setEnabled(state);
}


void KChartView::slotConfigPageLayout()
{
    KChartParams      *params = ((KChartPart*)koDocument())->params();
    KChartPageLayout  *dialog = new KChartPageLayout(params, this,
						     "Page Layout");

    connect( dialog, TQ_SIGNAL( dataChanged() ),
             this,   TQ_SLOT( slotRepaint() ) );

    dialog->exec();
    delete dialog;
}


void KChartView::setupPrinter( KPrinter &printer )
{
  if ( !printer.previewOnly() )
    printer.addDialogPage( new KChartPrinterDlg( 0, "KChart page" ) );
}


void KChartView::print(KPrinter &printer)
{
    printer.setFullPage( false );

    TQPainter painter;
    painter.begin(&printer);
    TQPaintDeviceMetrics  pdm( &printer );

    int  height;
    int  width;
    if ( !printer.previewOnly() ) {
	int const scalex = printer.option("kde-kchart-printsizex").toInt();
	int const scaley = printer.option("kde-kchart-printsizey").toInt();

	width  = pdm.width()  * scalex / 100;
	height = pdm.height() * scaley / 100;
    }
    else {
	// Fill the whole page.
	width  = pdm.width();
	height = pdm.height();
    }

    TQRect  rect(0, 0, width, height);
    KDChart::print(&painter,
		   ((KChartPart*)koDocument())->params(),
		   ((KChartPart*)koDocument())->data(),
		   0, 		// regions
		   &rect);

    painter.end();
}


// Import data from a Comma Separated Values file.
//

void KChartView::importData()
{
    // Get the name of the file to open.
    TQString filename = KFileDialog::getOpenFileName(TQString(),// startDir
						    TQString(),// filter
						    0,
						    i18n("Import Data"));
    kdDebug(35001) << "Filename = <" << filename << ">" << endl;
    if (filename.isEmpty())
      return;

    // Check to see if we can read the file.
    TQFile  inFile(filename);
    if (!inFile.open(IO_ReadOnly)) {
	KMessageBox::sorry( 0, i18n("The file %1 could not be read.")
			    .arg(filename) );
	inFile.close();
	return;
    }

    // Let the CSV dialog structure the data in the file.
    TQByteArray  inData( inFile.readAll() );
    inFile.close();
    CSVImportDialog  *dialog = new CSVImportDialog(0L, inData);

    if ( !dialog->exec() ) {
	kdDebug(35001) << "Cancel was pressed" << endl;
	return;
    }

    kdDebug(35001) << "OK was pressed" << endl;

    uint  rows = dialog->rows();
    uint  cols = dialog->cols();

    kdDebug(35001) << "Rows: " << rows << "  Cols: " << cols << endl;

    bool  hasRowHeaders = ( rows > 1 && dialog->firstRowContainHeaders() );
    bool  hasColHeaders = ( cols > 1 && dialog->firstColContainHeaders() );

    KDChartTableData  data( rows, cols );
    data.setUsedRows( rows );
    data.setUsedCols( cols );
    for (uint row = 0; row < rows; row++) {
	for (uint col = 0; col < cols; col++) {
	    bool     ok;
	    TQString  tmp;
	    double   val;

	    // Get the text and convert to double unless in the headers.
	    tmp = dialog->text( row, col );
	    if ( ( row == 0 && hasRowHeaders )
		 || ( col == 0 && hasColHeaders ) ) {
		kdDebug(35001) << "Setting header (" << row << "," << col
			       << ") to value " << tmp << endl;
		data.setCell( row, col, tmp );
	    }
	    else {
		val = tmp.toDouble(&ok);
		if (!ok)
		    val = 0.0;

		kdDebug(35001) << "Setting (" << row << "," << col
			       << ") to value " << val << endl;

		// and do the actual setting.
		data.setCell( row, col, val );
	    }
	}
    }

    ((KChartPart*)koDocument())->doSetData( data,
					    hasRowHeaders, hasColHeaders );
}


void KChartView::extraCreateTemplate()
{
    int width = 60;
    int height = 60;
    TQPixmap pix = koDocument()->generatePreview(TQSize(width, height));

    KTempFile tempFile( TQString(), ".chrt" );
    tempFile.setAutoDelete(true);

    koDocument()->saveNativeFormat( tempFile.name() );

    KoTemplateCreateDia::createTemplate( "kchart_template", KChartFactory::global(),
                                         tempFile.name(), pix, this );

    KChartFactory::global()->dirs()->addResourceType("kchart_template",
                                                    TDEStandardDirs::kde_default( "data" ) +
                                                    "kchart/templates/");
}


}  //KChart namespace

#include "kchart_view.moc"
