//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2002-2017 Werner Schweer
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License version 2
//  as published by the Free Software Foundation and appearing in
//  the file LICENCE.GPL
//=============================================================================

#include "globals.h"
#include "pagesettings.h"
#include "libmscore/page.h"
#include "libmscore/style.h"
#include "libmscore/score.h"
#include "navigator.h"
#include "libmscore/mscore.h"
#include "libmscore/excerpt.h"
#include "musescore.h"

namespace Ms {

//---------------------------------------------------------
//   PageSettings
//---------------------------------------------------------

PageSettings::PageSettings(QWidget* parent)
   : AbstractDialog(parent)
      {
      setObjectName("PageSettings");
      setupUi(this);
      setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
      setModal(true);

      NScrollArea* sa = new NScrollArea;
      preview = new Navigator(sa, this);
      preview->setPreviewOnly(true);

      static_cast<QVBoxLayout*>(previewGroup->layout())->insertWidget(0, sa);

      mmUnit = true;      // should be made a global configuration item

      if (mmUnit)
            mmButton->setChecked(true);
      else
            inchButton->setChecked(true);

      MuseScore::restoreGeometry(this);

      for (int i = 0; i < QPageSize::LastPageSize; ++i)
            pageGroup->addItem(QPageSize::name(QPageSize::PageSizeId(i)), i);

      connect(mmButton,             SIGNAL(clicked()),            SLOT(mmClicked()));
      connect(inchButton,           SIGNAL(clicked()),            SLOT(inchClicked()));
      connect(buttonApply,          SIGNAL(clicked()),            SLOT(apply()));
      connect(buttonApplyToAllParts,SIGNAL(clicked()),            SLOT(applyToAllParts()));
      connect(buttonOk,             SIGNAL(clicked()),            SLOT(ok()));
      connect(portraitButton,       SIGNAL(clicked()),            SLOT(orientationClicked()));
      connect(landscapeButton,      SIGNAL(clicked()),            SLOT(orientationClicked()));
      connect(twosided,             SIGNAL(toggled(bool)),        SLOT(twosidedToggled(bool)));
      connect(pageHeight,           SIGNAL(valueChanged(double)), SLOT(pageHeightChanged(double)));
      connect(pageWidth,            SIGNAL(valueChanged(double)), SLOT(pageWidthChanged(double)));
      connect(oddPageTopMargin,     SIGNAL(valueChanged(double)), SLOT(otmChanged(double)));
      connect(oddPageBottomMargin,  SIGNAL(valueChanged(double)), SLOT(obmChanged(double)));
      connect(oddPageLeftMargin,    SIGNAL(valueChanged(double)), SLOT(olmChanged(double)));
      connect(oddPageRightMargin,   SIGNAL(valueChanged(double)), SLOT(ormChanged(double)));
      connect(evenPageTopMargin,    SIGNAL(valueChanged(double)), SLOT(etmChanged(double)));
      connect(evenPageBottomMargin, SIGNAL(valueChanged(double)), SLOT(ebmChanged(double)));
      connect(evenPageRightMargin,  SIGNAL(valueChanged(double)), SLOT(ermChanged(double)));
      connect(evenPageLeftMargin,   SIGNAL(valueChanged(double)), SLOT(elmChanged(double)));
      connect(pageGroup,            SIGNAL(activated(int)),       SLOT(pageFormatSelected(int)));
      connect(spatiumEntry,         SIGNAL(valueChanged(double)), SLOT(spatiumChanged(double)));
      connect(pageOffsetEntry,      SIGNAL(valueChanged(int)),    SLOT(pageOffsetChanged(int)));
      }

//---------------------------------------------------------
//   PageSettings
//---------------------------------------------------------

PageSettings::~PageSettings()
      {
      }

//---------------------------------------------------------
//   hideEvent
//---------------------------------------------------------

void PageSettings::hideEvent(QHideEvent* ev)
      {
      MuseScore::saveGeometry(this);
      QWidget::hideEvent(ev);
      }

//---------------------------------------------------------
//   setScore
//---------------------------------------------------------

void PageSettings::setScore(MasterScore* s)
      {
      cs  = s;
      MasterScore* sl = s->clone();
      preview->setScore(sl);
      buttonApplyToAllParts->setEnabled(!s->isMaster());
      updateValues();
      updatePreview(0);
      }

//---------------------------------------------------------
//   blockSignals
//---------------------------------------------------------

void PageSettings::blockSignals(bool block)
      {
      for (auto w : { oddPageTopMargin, oddPageBottomMargin, oddPageLeftMargin, oddPageRightMargin,
         evenPageTopMargin, evenPageBottomMargin, evenPageLeftMargin, evenPageRightMargin, spatiumEntry,
         pageWidth, pageHeight } )
            {
            w->blockSignals(block);
            }
      pageOffsetEntry->blockSignals(block);
      pageGroup->blockSignals(block);
      }

//---------------------------------------------------------
//   setMarginsMax
//---------------------------------------------------------

void PageSettings::setMarginsMax(double pw)
      {
      oddPageLeftMargin->setMaximum(pw);
      oddPageRightMargin->setMaximum(pw);
      evenPageLeftMargin->setMaximum(pw);
      evenPageRightMargin->setMaximum(pw);
      }

//---------------------------------------------------------
//   updateValues
//    set gui values from style
//---------------------------------------------------------

void PageSettings::updateValues()
      {
      Score* score = preview->score();
      bool mm = mmButton->isChecked();

      blockSignals(true);

      const char* suffix;
      double singleStepSize;
      double singleStepScale;
      if (mm) {
            suffix = "mm";
            singleStepSize = 1.0;
            singleStepScale = 0.2;
            }
      else {
            suffix = "in";
            singleStepSize = 0.05;
            singleStepScale = 0.005;
            }
      for (auto w : { oddPageTopMargin, oddPageBottomMargin, oddPageLeftMargin, oddPageRightMargin, evenPageTopMargin,
         evenPageBottomMargin, evenPageLeftMargin, evenPageRightMargin, spatiumEntry, pageWidth, pageHeight } )
            {
            w->setSuffix(suffix);
            w->setSingleStep(singleStepSize);
            }
      spatiumEntry->setSingleStep(singleStepScale);

      double f = mm ? INCH : 1.0;

      double w = score->styleD(StyleIdx::pageWidth);
      double h = score->styleD(StyleIdx::pageHeight);
      setMarginsMax(w * f);

      double pw = score->styleD(StyleIdx::pagePrintableWidth);
      pageGroup->setCurrentIndex(int(QPageSize::id(QSizeF(w, h), QPageSize::Inch, QPageSize::FuzzyOrientationMatch)));

      oddPageTopMargin->setValue(score->styleD(StyleIdx::pageOddTopMargin) * f);
      oddPageBottomMargin->setValue(score->styleD(StyleIdx::pageOddBottomMargin) * f);
      oddPageLeftMargin->setValue(score->styleD(StyleIdx::pageOddLeftMargin) * f);
      oddPageRightMargin->setValue((w - pw - score->styleD(StyleIdx::pageOddLeftMargin)) * f);

      evenPageTopMargin->setValue(score->styleD(StyleIdx::pageEvenTopMargin) * f);
      evenPageBottomMargin->setValue(score->styleD(StyleIdx::pageEvenBottomMargin) * f);
      evenPageLeftMargin->setValue(score->styleD(StyleIdx::pageEvenLeftMargin) * f);
      evenPageRightMargin->setValue((w - pw - score->styleD(StyleIdx::pageEvenLeftMargin)) * f);
      pageHeight->setValue(h * f);
      pageWidth->setValue(w * f);

      double f1 = mm ? 1.0/DPMM : 1.0/DPI;
      spatiumEntry->setValue(score->spatium() * f1);


      bool _twosided = score->styleB(StyleIdx::pageTwosided);
      evenPageTopMargin->setEnabled(_twosided);
      evenPageBottomMargin->setEnabled(_twosided);
      evenPageLeftMargin->setEnabled(_twosided);
      evenPageRightMargin->setEnabled(_twosided);

      if (twosided->isChecked()) {
            evenPageRightMargin->setValue(oddPageLeftMargin->value());
            evenPageLeftMargin->setValue(oddPageRightMargin->value());
            }
      else {
            evenPageRightMargin->setValue(oddPageRightMargin->value());
            evenPageLeftMargin->setValue(oddPageLeftMargin->value());
            }

      landscapeButton->setChecked(score->styleD(StyleIdx::pageWidth) > score->styleD(StyleIdx::pageHeight));
      portraitButton->setChecked(score->styleD(StyleIdx::pageWidth) <= score->styleD(StyleIdx::pageHeight));

      twosided->setChecked(_twosided);

      pageOffsetEntry->setValue(score->pageNumberOffset() + 1);

      blockSignals(false);
      }

//---------------------------------------------------------
//   inchClicked
//---------------------------------------------------------

void PageSettings::inchClicked()
      {
      mmUnit = false;
      updateValues();
      }

//---------------------------------------------------------
//   mmClicked
//---------------------------------------------------------

void PageSettings::mmClicked()
      {
      mmUnit = true;
      updateValues();
      }

//---------------------------------------------------------
//   orientationClicked
//    swap width/height
//---------------------------------------------------------

void PageSettings::orientationClicked()
      {
      qreal w = preview->score()->styleD(StyleIdx::pageWidth);
      qreal h = preview->score()->styleD(StyleIdx::pageHeight);

      preview->score()->style().set(StyleIdx::pageWidth, h);
      preview->score()->style().set(StyleIdx::pageHeight, w);

      double f = mmUnit ? 1.0/INCH : 1.0;
      preview->score()->style().set(StyleIdx::pagePrintableWidth, h - (oddPageLeftMargin->value() + oddPageRightMargin->value()) * f);
      updateValues();
      updatePreview(0);
      }

//---------------------------------------------------------
//   twosidedToggled
//---------------------------------------------------------

void PageSettings::twosidedToggled(bool flag)
      {
      preview->score()->style().set(StyleIdx::pageTwosided, flag);
      updateValues();
      updatePreview(1);
      }

//---------------------------------------------------------
//   apply
//---------------------------------------------------------

void PageSettings::apply()
      {
      applyToScore(cs);
      mscore->endCmd();
      }

//---------------------------------------------------------
//   applyToScore
//---------------------------------------------------------

void PageSettings::applyToScore(Score* s)
      {
      s->startCmd();
      double f  = mmUnit ? 1.0/INCH : 1.0;
      double f1 = mmUnit ? DPMM : DPI;

      s->undoChangeStyleVal(StyleIdx::pageWidth, pageWidth->value() * f);
      s->undoChangeStyleVal(StyleIdx::pageHeight, pageHeight->value() * f);
      s->undoChangeStyleVal(StyleIdx::pagePrintableWidth, (pageWidth->value() - oddPageLeftMargin->value() - oddPageRightMargin->value())  * f);
      s->undoChangeStyleVal(StyleIdx::pageEvenTopMargin, evenPageTopMargin->value() * f);
      s->undoChangeStyleVal(StyleIdx::pageEvenBottomMargin, evenPageBottomMargin->value() * f);
      s->undoChangeStyleVal(StyleIdx::pageEvenLeftMargin, evenPageLeftMargin->value() * f);
      s->undoChangeStyleVal(StyleIdx::pageOddTopMargin, oddPageTopMargin->value() * f);
      s->undoChangeStyleVal(StyleIdx::pageOddBottomMargin, oddPageBottomMargin->value() * f);
      s->undoChangeStyleVal(StyleIdx::pageTwosided, twosided->isChecked());
      s->undoChangeStyleVal(StyleIdx::spatium, spatiumEntry->value() * f1);

      s->endCmd();
      }

//---------------------------------------------------------
//   applyToAllParts
//---------------------------------------------------------

void PageSettings::applyToAllParts()
      {
      for (Excerpt* e : cs->excerpts())
            applyToScore(e->partScore());
      }

//---------------------------------------------------------
//   ok
//---------------------------------------------------------

void PageSettings::ok()
      {
      apply();
      done(0);
      }

//---------------------------------------------------------
//   done
//---------------------------------------------------------

void PageSettings::done(int val)
      {
      cs->setLayoutAll();     // HACK
      QDialog::done(val);
      }

//---------------------------------------------------------
//   pageFormatSelected
//---------------------------------------------------------

void PageSettings::pageFormatSelected(int size)
      {
      if (size >= 0) {
            Score* s  = preview->score();
            int id    = pageGroup->currentData().toInt();
            QSizeF sz = QPageSize::size(QPageSize::PageSizeId(id), QPageSize::Inch);

            s->style().set(StyleIdx::pageWidth, sz.width());
            s->style().set(StyleIdx::pageHeight, sz.height());

            double f  = mmUnit ? 1.0/INCH : 1.0;
            s->style().set(StyleIdx::pagePrintableWidth, sz.width() - (oddPageLeftMargin->value() + oddPageRightMargin->value())  * f);
            updateValues();
            updatePreview(0);
            }
      }

//---------------------------------------------------------
//   otmChanged
//---------------------------------------------------------

void PageSettings::otmChanged(double val)
      {
      if (mmUnit)
            val /= INCH;
      preview->score()->style().set(StyleIdx::pageOddTopMargin, val);
      updatePreview(1);
      }

//---------------------------------------------------------
//   olmChanged
//---------------------------------------------------------

void PageSettings::olmChanged(double val)
      {
      if (mmUnit)
            val /= INCH;

      if (twosided->isChecked()) {
            evenPageRightMargin->blockSignals(true);
            evenPageRightMargin->setValue(val * (mmUnit ? INCH : 1.0));
            evenPageRightMargin->blockSignals(false);
            }
      else{
            evenPageLeftMargin->blockSignals(true);
            evenPageLeftMargin->setValue(val * (mmUnit ? INCH : 1.0));
            evenPageLeftMargin->blockSignals(false);
            }
      Score* s = preview->score();
      s->style().set(StyleIdx::pageOddLeftMargin, val);
      s->style().set(StyleIdx::pagePrintableWidth, s->styleD(StyleIdx::pageWidth) - s->styleD(StyleIdx::pageEvenLeftMargin) - val);

      updatePreview(0);
      }

//---------------------------------------------------------
//   ormChanged
//---------------------------------------------------------

void PageSettings::ormChanged(double val)
      {
      if (mmUnit)
            val /= INCH;

      Score* s = preview->score();
      if (twosided->isChecked()) {
            evenPageLeftMargin->blockSignals(true);
            evenPageLeftMargin->setValue(val * (mmUnit ? INCH : 1.0));
            s->style().set(StyleIdx::pageEvenLeftMargin, val);
            evenPageLeftMargin->blockSignals(false);
            }
      else {
            evenPageRightMargin->blockSignals(true);
            evenPageRightMargin->setValue(val * (mmUnit ? INCH : 1.0));
            evenPageRightMargin->blockSignals(false);
            }
      s->style().set(StyleIdx::pagePrintableWidth, s->styleD(StyleIdx::pageWidth) - s->styleD(StyleIdx::pageOddLeftMargin) - val);
      updatePreview(0);
      }

//---------------------------------------------------------
//   obmChanged
//---------------------------------------------------------

void PageSettings::obmChanged(double val)
      {
      if (mmUnit)
            val /= INCH;
      preview->score()->style().set(StyleIdx::pageOddBottomMargin, val);
      updatePreview(1);
      }

//---------------------------------------------------------
//   etmChanged
//---------------------------------------------------------

void PageSettings::etmChanged(double val)
      {
      if (mmUnit)
            val /= INCH;
      preview->score()->style().set(StyleIdx::pageEvenTopMargin, val);
      updatePreview(1);
      }

//---------------------------------------------------------
//   elmChanged
//---------------------------------------------------------

void PageSettings::elmChanged(double val)
      {
      double f  = mmUnit ? 1.0/INCH : 1.0;
      val *= f;

      if (twosided->isChecked()) {
            oddPageRightMargin->blockSignals(true);
            oddPageRightMargin->setValue(val * (mmUnit ? INCH : 1.0));
            oddPageRightMargin->blockSignals(false);
            }
      Score* s = preview->score();
      s->style().set(StyleIdx::pageEvenLeftMargin, val);
      s->style().set(StyleIdx::pagePrintableWidth, s->styleD(StyleIdx::pageWidth) - evenPageRightMargin->value() * f - val);
      updatePreview(0);
      }

//---------------------------------------------------------
//   ermChanged
//---------------------------------------------------------

void PageSettings::ermChanged(double val)
      {
      if (mmUnit)
            val /= INCH;

      if (twosided->isChecked()) {
            oddPageLeftMargin->blockSignals(true);
            oddPageLeftMargin->setValue(val * (mmUnit ? INCH : 1.0));
            oddPageLeftMargin->blockSignals(false);
            }
      Score* s = preview->score();
      s->style().set(StyleIdx::pagePrintableWidth, s->styleD(StyleIdx::pageWidth) - s->styleD(StyleIdx::pageEvenLeftMargin) - val);
      s->style().set(StyleIdx::pageOddLeftMargin, val);
      updatePreview(0);
      }

//---------------------------------------------------------
//   ebmChanged
//---------------------------------------------------------

void PageSettings::ebmChanged(double val)
      {
      if (mmUnit)
            val /= INCH;
      preview->score()->style().set(StyleIdx::pageEvenBottomMargin, val);
      updatePreview(1);
      }

//---------------------------------------------------------
//   spatiumChanged
//---------------------------------------------------------

void PageSettings::spatiumChanged(double val)
      {
      val *= mmUnit ? DPMM : DPI;
      double oldVal = preview->score()->spatium();
      preview->score()->setSpatium(val);
      preview->score()->spatiumChanged(oldVal, val);
      updatePreview(0);
      }

//---------------------------------------------------------
//   pageOffsetChanged
//---------------------------------------------------------

void PageSettings::pageOffsetChanged(int val)
      {
      preview->score()->setPageNumberOffset(val-1);
      updatePreview(0);
      }

//---------------------------------------------------------
//   pageHeightChanged
//---------------------------------------------------------

void PageSettings::pageHeightChanged(double val)
      {
      double val2 = pageWidth->value();
      if (mmUnit) {
            val /= INCH;
            val2 /= INCH;
            }
      pageGroup->setCurrentIndex(0);      // custom
      preview->score()->style().set(StyleIdx::pageWidth, val);
      preview->score()->style().set(StyleIdx::pageHeight, val2);

      updatePreview(1);
      }

//---------------------------------------------------------
//   pageWidthChanged
//---------------------------------------------------------

void PageSettings::pageWidthChanged(double val)
      {
      setMarginsMax(val);

      double f    = mmUnit ? 1.0/INCH : 1.0;
      double val2 = pageHeight->value() * f;
      val        *= f;
      pageGroup->setCurrentIndex(0);      // custom
      preview->score()->style().set(StyleIdx::pageWidth, val);
      preview->score()->style().set(StyleIdx::pageHeight, val2);
      preview->score()->style().set(StyleIdx::pagePrintableWidth, val - (oddPageLeftMargin->value() + evenPageLeftMargin->value()) * f);
      updatePreview(0);
      }

//---------------------------------------------------------
//   updatePreview
//---------------------------------------------------------

void PageSettings::updatePreview(int val)
      {
      updateValues();
      switch(val) {
            case 0:
                  preview->score()->doLayout();
                  break;
            case 1:
                  preview->score()->doLayout();
                  break;
            }
      preview->layoutChanged();
      }
}

