package MainWindow;

use strict;
use warnings;
use QtCore4;
use QtGui4;
use Ui_MainWindowBase;
use QtCore4::isa qw( Qt::MainWindow );
use QtCore4::slots
    on_clearAction_triggered => [],
    on_markAction_triggered => [],
    on_printAction_triggered => [],
    on_printPreviewAction_triggered => [],
    on_unmarkAction_triggered => [],
    printDocument => ['QPrinter *'],
    printPage => ['int', 'QPainter *', 'QPrinter *'],
    showFont => ['QTreeWidgetItem *'],
    updateStyles => ['QTreeWidgetItem *', 'int'];
use List::Util qw(min max);

sub sampleSizes() {
    return this->{sampleSizes};
}

sub pageMap() {
    return this->{pageMap};
}

sub markedCount() {
    return this->{markedCount};
}

sub NEW
{
    my ($class, $parent) = @_;
    $class->SUPER::NEW($parent);
    this->{ui} = Ui_MainWindowBase->setupUi(this);

    this->{sampleSizes} = [qw( 32 24 16 14 12 8 4 2 1 )];
    this->{markedCount} = 0;
    this->setupFontTree();

    this->connect(this->{ui}->quitAction, SIGNAL 'triggered()', qApp, SLOT 'quit()');
    this->connect(this->{ui}->fontTree, SIGNAL 'currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)',
            this, SLOT 'showFont(QTreeWidgetItem *)');
    this->connect(this->{ui}->fontTree, SIGNAL 'itemChanged(QTreeWidgetItem *, int)',
            this, SLOT 'updateStyles(QTreeWidgetItem *, int)');

    this->{ui}->fontTree->setItemSelected(this->{ui}->fontTree->topLevelItem(0), 1);
    this->showFont(this->{ui}->fontTree->topLevelItem(0));
}

sub setupFontTree
{
    my $database = Qt::FontDatabase();
    this->{ui}->fontTree->setColumnCount(1);
    this->{ui}->fontTree->setHeaderLabels([this->tr('Font')]);

    foreach my $family ( @{$database->families()} ) {
        my $styles = $database->styles($family);
        if (!$styles or ref $styles ne 'ARRAY') {
            next;
        }

        my $familyItem = Qt::TreeWidgetItem(this->{ui}->fontTree);
        $familyItem->setText(0, $family);
        $familyItem->setCheckState(0, Qt::Unchecked());

        foreach my $style (@{$styles}) {
            my $styleItem = Qt::TreeWidgetItem($familyItem);
            $styleItem->setText(0, $style);
            $styleItem->setCheckState(0, Qt::Unchecked());
            $styleItem->setData(0, Qt::UserRole(),
                Qt::Variant(Qt::Int($database->weight($family, $style))));
            $styleItem->setData(0, Qt::UserRole() + 1,
                Qt::Variant(Qt::Bool($database->italic($family, $style)?1:0)));
        }
    }
}

sub on_clearAction_triggered
{
    my $currentItem = this->{ui}->fontTree->currentItem();
    foreach my $item (@{this->{ui}->fontTree->selectedItems()}) {
        this->{ui}->fontTree->setItemSelected($item, 0);
    }
    this->{ui}->fontTree->setItemSelected($currentItem, 1);
}

sub on_markAction_triggered
{
    this->markUnmarkFonts(Qt::Checked());
}

sub on_unmarkAction_triggered
{
    this->markUnmarkFonts(Qt::Unchecked());
}

sub markUnmarkFonts
{
    my ($state) = @_;
    my $items = this->{ui}->fontTree->selectedItems();
    foreach my $item (@{$items}) {
        if ($item->checkState(0) != $state) {
            $item->setCheckState(0, $state);
        }
    }
}

sub showFont
{
    my ($item) = @_;
    if (!$item) {
        return;
    }

    my $family;
    my $style;
    my $weight;
    my $italic;

    if ($item->parent()) {
        $family = $item->parent()->text(0);
        $style = $item->text(0);
        $weight = $item->data(0, Qt::UserRole())->toInt();
        $italic = $item->data(0, Qt::UserRole() + 1)->toBool();
    } else {
        $family = $item->text(0);
        $style = $item->child(0)->text(0);
        $weight = $item->child(0)->data(0, Qt::UserRole())->toInt();
        $italic = $item->child(0)->data(0, Qt::UserRole() + 1)->toBool();
    }

    my $oldText = this->{ui}->textEdit->toPlainText();
    $oldText =~ s/[\s]+$//g;
    my $modified = this->{ui}->textEdit->document()->isModified();
    this->{ui}->textEdit->clear();
    this->{ui}->textEdit->document()->setDefaultFont(Qt::Font($family, 32, $weight, $italic));

    my $cursor = this->{ui}->textEdit->textCursor();
    my $blockFormat = Qt::TextBlockFormat();
    $blockFormat->setAlignment(Qt::AlignCenter());
    $cursor->insertBlock($blockFormat);

    if ($modified) {
        $cursor->insertText($oldText);
    }
    else {
        $cursor->insertText("$family $style");
    }

    this->{ui}->textEdit->document()->setModified($modified);
}

sub updateStyles
{
    my ($item, $column) = @_;
    if (!$item || $column != 0) {
        return;
    }

    my $state = $item->checkState(0);
    my $parent = $item->parent();

    if ($parent) {

        # Only count style items.
        if ($state == Qt::Checked()) {
            ++(this->{markedCount});
        }
        else {
            --(this->{markedCount});
        }

        if ($state == Qt::Checked() &&
            $parent->checkState(0) == Qt::Unchecked()) {
            # Mark parent items when child items are checked.
            $parent->setCheckState(0, Qt::Checked());

        } elsif ($state == Qt::Unchecked() &&
                 $parent->checkState(0) == Qt::Checked()) {

            my $marked = 0;
            for (my $row = 0; $row < $parent->childCount(); ++$row) {
                if ($parent->child($row)->checkState(0) == Qt::Checked()) {
                    $marked = 1;
                    last;
                }
            }
            # Unmark parent items when all child items are unchecked.
            if (!$marked) {
                $parent->setCheckState(0, Qt::Unchecked());
            }
        }
    } else {
        my $row;
        my $number = 0;
        for ($row = 0; $row < $item->childCount(); ++$row) {
            if ($item->child($row)->checkState(0) == Qt::Checked()) {
                ++$number;
            }
        }

        # Mark/unmark all child items when marking/unmarking top-level
        # items.
        if ($state == Qt::Checked() && $number == 0) {
            for ($row = 0; $row < $item->childCount(); ++$row) {
                if ($item->child($row)->checkState(0) == Qt::Unchecked()) {
                    $item->child($row)->setCheckState(0, Qt::Checked());
                }
            }
        } elsif ($state == Qt::Unchecked() && $number > 0) {
            for ($row = 0; $row < $item->childCount(); ++$row) {
                if ($item->child($row)->checkState(0) == Qt::Checked()) {
                    $item->child($row)->setCheckState(0, Qt::Unchecked());
                }
            }
        }
    }

    this->{ui}->printAction->setEnabled(this->markedCount > 0);
    this->{ui}->printPreviewAction->setEnabled(this->markedCount > 0);
}

sub on_printAction_triggered
{
    this->{pageMap} = this->currentPageMap();

    if (scalar keys %{this->pageMap} == 0) {
        return;
    }

    my $printer = Qt::Printer(Qt::Printer::HighResolution());
    my $dialog = Qt::PrintDialog($printer, this);
    if ($dialog->exec() != Qt::Dialog::Accepted()) {
        return;
    }

    my $from = $printer->fromPage();
    my $to = $printer->toPage();
    if ($from <= 0 && $to <= 0) {
        $printer->setFromTo(1, scalar keys %{this->pageMap});
    }

    this->printDocument($printer);
}

sub printDocument
{
    my ($printer) = @_;
    $printer->setFromTo(1, scalar keys %{this->pageMap});

    my $progress = Qt::ProgressDialog(this->tr('Preparing font samples...'), this->tr('&Cancel'),
                             0, scalar keys %{this->pageMap}, this);
    $progress->setWindowModality(Qt::ApplicationModal());
    $progress->setWindowTitle(this->tr('Font Sampler'));
    $progress->setMinimum($printer->fromPage() - 1);
    $progress->setMaximum($printer->toPage());

    my $painter = Qt::Painter();
    $painter->begin($printer);
    my $firstPage = 1;

    for (my $page = $printer->fromPage(); $page <= $printer->toPage(); ++$page) {

        if (!$firstPage) {
            $printer->newPage();
        }

        qApp->processEvents();
        if ($progress->wasCanceled()) {
            last;
        }

        this->printPage($page - 1, $painter, $printer);
        $progress->setValue($page);
        $firstPage = 0;
    }

    $painter->end();
}

sub on_printPreviewAction_triggered
{
    this->{pageMap} = this->currentPageMap();

    if (scalar keys %{this->pageMap} == 0) {
        return;
    }

    my $printer = Qt::Printer(Qt::Printer::HighResolution());
    my $preview = Qt::PrintPreviewDialog($printer, this);
    this->connect($preview, SIGNAL 'paintRequested(QPrinter *)',
            this, SLOT 'printDocument(QPrinter *)');
    $preview->exec();
}

sub currentPageMap
{
    my %pageMap;

    for (my $row = 0; $row < this->{ui}->fontTree->topLevelItemCount(); ++$row) {
        my $familyItem = this->{ui}->fontTree->topLevelItem($row);
        my $family;

        if ($familyItem->checkState(0) == Qt::Checked()) {
            $family = $familyItem->text(0);
            $pageMap{$family} = [];
        }

        for (my $childRow = 0; $childRow < $familyItem->childCount(); ++$childRow) {
            my $styleItem = $familyItem->child($childRow);
            if ($styleItem->checkState(0) == Qt::Checked()) {
                push @{$pageMap{$family}}, $styleItem;
            }
        }
    }

    return \%pageMap;
}

sub printPage
{
    my ($index, $painter, $printer) = @_;
    my $family = (keys %{this->pageMap})[$index];
    my @items = @{this->pageMap->{$family}};

    # Find the dimensions of the text on each page.
    my $width = 0.0;
    my $height = 0.0;
    foreach my $item (@items) {
        my $style = $item->text(0);
        my $weight = $item->data(0, Qt::UserRole())->toInt();
        my $italic = $item->data(0, Qt::UserRole() + 1)->toBool();

        # Calculate the maximum width and total height of the text.
        foreach my $size ( @{this->sampleSizes} ) {
            my $font = Qt::Font($family, $size, $weight, $italic);
            $font = Qt::Font($font, $painter->device());
            my $fontMetrics = Qt::FontMetricsF($font);
            my $rect = $fontMetrics->boundingRect( "$family $style" );
            $width = max($rect->width(), $width);
            $height += $rect->height();
        }
    }

    my $xScale = $printer->pageRect()->width() / $width;
    my $yScale = $printer->pageRect()->height() / $height;
    my $scale = min($xScale, $yScale);

    my $remainingHeight = $printer->pageRect()->height()/$scale - $height;
    my $spaceHeight = ($remainingHeight/4.0) / scalar @items + 1;
    my $interLineHeight = ($remainingHeight/4.0) / (scalar @{this->sampleSizes} * scalar @items);

    $painter->save();
    $painter->translate($printer->pageRect()->width()/2.0, $printer->pageRect()->height()/2.0);
    $painter->scale($scale, $scale);
    $painter->setBrush(Qt::Brush(Qt::black()));

    my $x = -$width/2.0;
    my $y = -$height/2.0 - $remainingHeight/4.0 + $spaceHeight;

    foreach my $item (@items) {
        my $style = $item->text(0);
        my $weight = $item->data(0, Qt::UserRole())->toInt();
        my $italic = $item->data(0, Qt::UserRole() + 1)->toBool();

        # Draw each line of text.
        foreach my $size (@{this->sampleSizes}) {
            my $font = Qt::Font($family, $size, $weight, $italic);
            $font = Qt::Font($font, $painter->device());
            my $fontMetrics = Qt::FontMetricsF($font);
            my $rect = $fontMetrics->boundingRect( "$family $style" );
            $y += $rect->height();
            $painter->setFont($font);
            $painter->drawText(Qt::PointF($x, $y),
                             "$family $style");
            $y += $interLineHeight;
        }
        $y += $spaceHeight;
    }

    $painter->restore();
}

1;