#include "modeleditor.h"
#include "newvoxeldialog.h"
#include "colorselector.h"

#include <q3popupmenu.h>
#include <qmenubar.h>
#include <q3hbox.h>
#include <qfile.h>
#include <qdatastream.h>
#include <qcolordialog.h>
#include <q3filedialog.h>
#include <q3valuelist.h>
#include <qlabel.h>
#include <q3vbox.h>
#include <QPixmap>


ModelEditor::ModelEditor( QWidget *parent, const char *name, Qt::WFlags f )
    : Q3MainWindow( parent, name, f ), currLayer(0), clip(0)
{
    Q3HBox *hb = new Q3HBox(this);
    setCentralWidget(hb);
    Q3VBox *vb = new Q3VBox(hb);

    new QLabel("Color Picker:", vb);
    ColorSelector *sel = new ColorSelector(vb);
    sel->setFixedSize( 280, 280 );
    connect( sel, SIGNAL(colorSelected(QColor)), this, SLOT( selectedColor(QColor)) );

    new QLabel("Software View:", vb);
    view = new VoxelView(vb);
    view->setFixedSize( 280, 280 );

    Q3VBox *vb2 = new Q3VBox(hb);
    new QLabel("Layer Editor", vb2);
    ge = new GridEditor(vb2);
    ge->setFixedSize( 280, 280 );
    connect(ge, SIGNAL(changed()), view, SLOT(updateView()) );

    new QLabel("OpenGL View:", vb2);
    glview = new GLVoxelView(vb2);
    glview->setFixedSize( 280, 280 );
    connect(ge, SIGNAL(changed()), glview, SLOT(updateGL()) );
    
    hb->setSpacing(2);
    vb->setSpacing(2);
    vb2->setSpacing(2);

    model = new VoxelData( 20, 20, 20 );    // 20x20x20 on startup, but can be anything

    initMenu();

    ax = 30;
    ay = 0;
    az = 0;

    ge->setMap( model->layer(currLayer), model->width(), model->height() );
    view->setVoxelModel( model );
    view->setViewAngles( ax, ay, az );
    glview->setVoxelModel( model );
    glview->setViewAngles( ax, ay, az );

    loadFile("example.vxl"); // for demo purposes, load an example file on start

    setMinimumSize( 600, 600 );
}


void ModelEditor::newVoxel()
{
    // create a new voxel model dialog, then create a new blank model with the given width, height, depth
    NewVoxelDialog *dlg = new NewVoxelDialog(this, 0, true);
    if (dlg->exec() == QDialog::Accepted) {
        model = new VoxelData(dlg->widthSpin->value(),
                dlg->heightSpin->value(),
                dlg->depthSpin->value());

        ge->setMap( model->layer(currLayer), model->width(), model->height() );
        view->setVoxelModel( model );
        view->setViewAngles( ax, ay, az );
        glview->setVoxelModel( model );
        glview->setViewAngles( ax, ay, az );
    }
}


void ModelEditor::save()
{
    // pick file to save model to and write it out 
    QString fn = Q3FileDialog::getSaveFileName( QDir::currentDirPath(), "*.vxl", this, 0, tr("Save Voxel") );
    if ( fn.isEmpty() )
        return;
    QFile f( fn );
    if ( f.open( QIODevice::WriteOnly ) ) {
        QDataStream ds( &f );
        model->write( ds );
    }
}


void ModelEditor::load()
{
    // pick file to load and load it
    QString fn = Q3FileDialog::getOpenFileName( QDir::currentDirPath(), "*.vxl", this, 0, tr("Save Voxel") );
    if ( fn.isEmpty() )
        return;
    loadFile(fn);
}


void ModelEditor::loadFile(QString fn)
{
    // open and read file
    QFile f( fn );
    if ( f.open( QIODevice::ReadOnly ) ) {
        QDataStream ds( &f );
        model->read( ds );
        ge->setMap( model->layer(currLayer), model->width(), model->height() );
        view->setVoxelModel( model );
        view->setViewAngles( ax, ay, az );
        glview->setVoxelModel( model );
        glview->setViewAngles( ax, ay, az );
    }
}


void ModelEditor::initMenu()
{
    // Setup the applications menus
    Q3PopupMenu *file = new Q3PopupMenu( this );
    file->insertItem( tr("&New"), this, SLOT(newVoxel()) );
    file->insertItem( tr("&Load"), this, SLOT(load()) );
    file->insertItem( tr("&Save"), this, SLOT(save()) );
    file->insertItem( tr("&Quit"), qApp, SLOT(quit()) );
    menuBar()->insertItem( tr("&File"), file );

    Q3PopupMenu *edit = new Q3PopupMenu( this );
    edit->insertItem( tr("&Copy"), this, SLOT(copy()), Qt::CTRL + Qt::Key_C );
    edit->insertItem( tr("&Paste"), this, SLOT(paste()), Qt::CTRL + Qt::Key_V );
    edit->insertSeparator();
    edit->insertItem( tr("Layer &up"), this, SLOT(up()), Qt::Key_Up );
    edit->insertItem( tr("Layer &down"), this, SLOT(down()), Qt::Key_Down );
    edit->insertItem( tr("Rotate &left"), this, SLOT(left()), Qt::Key_Left );
    edit->insertItem( tr("Rotate &right"), this, SLOT(right()), Qt::Key_Right );
    edit->insertItem( tr("Rotate &up"), this, SLOT(tiltup()), Qt::Key_A );
    edit->insertItem( tr("Rotate &down"), this, SLOT(tiltdown()), Qt::Key_Z );
    edit->insertItem( tr("Rotate &cw"), this, SLOT(cw()), Qt::Key_W );
    edit->insertItem( tr("Rotate &ccw"), this, SLOT(ccw()), Qt::Key_Q );
    menuBar()->insertItem( tr("&Edit"), edit );
}


// up key pressed, move edit plane up one voxel
void ModelEditor::up()
{
    if ( currLayer > 0 ) {
        currLayer--;
        ge->setMap( model->layer(currLayer), model->width(), model->height() );
        glview->setCurrentLayer( currLayer );
    }
}


// down key pressed, move edit plane down one voxel
void ModelEditor::down()
{
    if ( currLayer < model->height()-1 ) {
        currLayer++;
        ge->setMap( model->layer(currLayer), model->width(), model->height() );
        glview->setCurrentLayer( currLayer );
    }
}


// left key pressed, spin around z-axis
void ModelEditor::left()
{
    az += 15;
    if ( az >= 360 )
        az = 0;
    view->setViewAngles( ax, ay, az );
    glview->setViewAngles( ax, ay, az );
}


// right key pressed, spin around z-axis
void ModelEditor::right()
{
    az -= 15;
    if ( az < 0 )
        az = 360-15;
    view->setViewAngles( ax, ay, az );
    glview->setViewAngles( ax, ay, az );
}


// 'a' key pressed, spin around x-axis
void ModelEditor::tiltup()
{
    ax += 15;
    if ( ax >= 360 )
        ax = 0;
    view->setViewAngles( ax, ay, az );
    glview->setViewAngles( ax, ay, az );
}


// 'z' key pressed, spin around x-axis
void ModelEditor::tiltdown()
{
    ax -= 15;
    if ( ax < 0 )
        ax = 360-15;
    view->setViewAngles( ax, ay, az );
    glview->setViewAngles( ax, ay, az );
}


// 'q' key pressed, spin around y-axis
void ModelEditor::cw() // clock-wise
{
    ay += 15;
    if ( ay >= 360 )
        ay = 0;
    view->setViewAngles( ax, ay, az );
    glview->setViewAngles( ax, ay, az );
}


// 'w' key pressed, spin around y-axis
void ModelEditor::ccw() // counter clock-wise
{
    ay -= 15;
    if ( ay < 0 )
        ay = 360-15;
    view->setViewAngles( ax, ay, az );
    glview->setViewAngles( ax, ay, az );
}


// copies the current plane of voxel data
void ModelEditor::copy()
{
    // XXX could use QClipboard, but it seems kind of pointless.
    if (!clip || clip->width() != model->width() || clip->height() != model->height()) {
        delete clip;
        clip = new VoxelData(model->width(), model->height(), 1);
    }

    for (int x = 0; x < model->width(); x++) {
        QRgb *src = model->layer(currLayer)[x];
        QRgb *dest = clip->layer(0)[x];
        memcpy(dest, src, model->height()*sizeof(QRgb));
    }
}


// pastes the last copied voxel data to the current plane
void ModelEditor::paste()
{
    if (clip && clip->width() == model->width() && clip->height() == model->height()) {
        for (int x = 0; x < model->width(); x++) {
            QRgb *src = clip->layer(0)[x];
            QRgb *dest = model->layer(currLayer)[x];
            memcpy(dest, src, model->height()*sizeof(QRgb));
        }
        glview->repaint(false);
        view->repaint(false);
    }
}


// when a color is picked, use that color in the editor
void ModelEditor::selectedColor(QColor c)
{
    ge->setColor( c.rgb() );
}

