//=============================================================================
//  MuseScore
//  Music Composition & Notation
//
//  Copyright (C) 2002-2011 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 "ottava.h"
#include "style.h"
#include "system.h"
#include "measure.h"
#include "xml.h"
#include "utils.h"
#include "score.h"
#include "text.h"
#include "staff.h"
#include "segment.h"
#include "sym.h"
#include "musescoreCore.h"

namespace Ms {

//---------------------------------------------------------
//   ottavaStyle
//---------------------------------------------------------

static const ElementStyle ottavaStyle {
      { Sid::ottavaNumbersOnly,                  Pid::NUMBERS_ONLY            },
      { Sid::ottava8VAPlacement,                 Pid::PLACEMENT               },
      { Sid::ottava8VAText,                      Pid::BEGIN_TEXT              },
      { Sid::ottava8VAText,                      Pid::CONTINUE_TEXT           },
      { Sid::ottavaHookAbove,                    Pid::END_HOOK_HEIGHT         },
      { Sid::ottavaFontFace,                     Pid::BEGIN_FONT_FACE         },
      { Sid::ottavaFontFace,                     Pid::CONTINUE_FONT_FACE      },
      { Sid::ottavaFontFace,                     Pid::END_FONT_FACE           },
      { Sid::ottavaFontSize,                     Pid::BEGIN_FONT_SIZE         },
      { Sid::ottavaFontSize,                     Pid::CONTINUE_FONT_SIZE      },
      { Sid::ottavaFontSize,                     Pid::END_FONT_SIZE           },
      { Sid::ottavaFontStyle,                    Pid::BEGIN_FONT_STYLE        },
      { Sid::ottavaFontStyle,                    Pid::CONTINUE_FONT_STYLE     },
      { Sid::ottavaFontStyle,                    Pid::END_FONT_STYLE          },
      { Sid::ottavaTextAlign,                    Pid::BEGIN_TEXT_ALIGN        },
      { Sid::ottavaTextAlign,                    Pid::CONTINUE_TEXT_ALIGN     },
      { Sid::ottavaTextAlign,                    Pid::END_TEXT_ALIGN          },
      { Sid::ottavaLineWidth,                    Pid::LINE_WIDTH              },
      { Sid::ottavaLineStyle,                    Pid::LINE_STYLE              },
      { Sid::ottavaPosAbove,                     Pid::OFFSET                  },
      };

//---------------------------------------------------------
//   layout
//---------------------------------------------------------

void OttavaSegment::layout()
      {
      TextLineBaseSegment::layout();
      autoplaceSpannerSegment();
      }

//---------------------------------------------------------
//   propertyDelegate
//---------------------------------------------------------

Element* OttavaSegment::propertyDelegate(Pid pid)
      {
      if (pid == Pid::OTTAVA_TYPE || pid == Pid::NUMBERS_ONLY)
            return spanner();
      return TextLineBaseSegment::propertyDelegate(pid);
      }

//---------------------------------------------------------
//   setOttavaType
//---------------------------------------------------------

void Ottava::setOttavaType(OttavaType val)
      {
      _ottavaType = val;
      }

//---------------------------------------------------------
//   setNumbersOnly
//---------------------------------------------------------

void Ottava::setNumbersOnly(bool val)
      {
      _numbersOnly = val;
      }

//---------------------------------------------------------
//   setPlacement
//---------------------------------------------------------

void Ottava::setPlacement(Placement p)
      {
      TextLineBase::setPlacement(p);
      }

//---------------------------------------------------------
//   undoChangeProperty
//---------------------------------------------------------

void OttavaSegment::undoChangeProperty(Pid id, const QVariant& v, PropertyFlags ps)
      {
      if (id == Pid::OTTAVA_TYPE || id == Pid::NUMBERS_ONLY) {
            ScoreElement::undoChangeProperty(id, v, ps);
            MuseScoreCore::mscoreCore->updateInspector();
            }
      else {
            ScoreElement::undoChangeProperty(id, v, ps);
            }
      }

void Ottava::undoChangeProperty(Pid id, const QVariant& v, PropertyFlags ps)
      {
      if (id == Pid::OTTAVA_TYPE || id == Pid::NUMBERS_ONLY) {
            TextLineBase::undoChangeProperty(id, v, ps);
            styleChanged();   // these properties may change style settings
            MuseScoreCore::mscoreCore->updateInspector();
            }
      else {
            TextLineBase::undoChangeProperty(id, v, ps);
            }
      }

//---------------------------------------------------------
//   getPropertyStyle
//---------------------------------------------------------

Sid OttavaSegment::getPropertyStyle(Pid pid) const
      {
      switch (pid) {
            case Pid::OFFSET:
                  return spanner()->placeAbove() ? Sid::ottavaPosAbove : Sid::ottavaPosBelow;
            default:
                  return TextLineBaseSegment::getPropertyStyle(pid);
            }
      }

Sid Ottava::getPropertyStyle(Pid pid) const
      {
      Q_ASSERT(int(OttavaType::OTTAVA_22MB) - int(OttavaType::OTTAVA_8VA) == 5);

      static const Sid ss[24] = {
            Sid::ottava8VAPlacement,
            Sid::ottava8VAnoText,
            Sid::ottava8VBPlacement,
            Sid::ottava8VBnoText,
            Sid::ottava15MAPlacement,
            Sid::ottava15MAnoText,
            Sid::ottava15MBPlacement,
            Sid::ottava15MBnoText,
            Sid::ottava22MAPlacement,
            Sid::ottava22MAnoText,
            Sid::ottava22MBPlacement,
            Sid::ottava22MBnoText,

            Sid::ottava8VAPlacement,
            Sid::ottava8VAText,
            Sid::ottava8VBPlacement,
            Sid::ottava8VBText,
            Sid::ottava15MAPlacement,
            Sid::ottava15MAText,
            Sid::ottava15MBPlacement,
            Sid::ottava15MBText,
            Sid::ottava22MAPlacement,
            Sid::ottava22MAText,
            Sid::ottava22MBPlacement,
            Sid::ottava22MBText,
            };

      switch (pid) {
            case Pid::OFFSET:
                  return placeAbove() ? Sid::ottavaPosAbove : Sid::ottavaPosBelow;
            case Pid::PLACEMENT: {
                  int idx = int(_ottavaType) * 2 + (_numbersOnly ? 0 : 12);
                  return ss[idx];
                  }
            case Pid::BEGIN_TEXT:
            case Pid::CONTINUE_TEXT: {
                  int idx = int(_ottavaType) * 2 + (_numbersOnly ? 0 : 12);
                  return ss[idx+1];       // BEGIN_TEXT
                  }
            case Pid::END_HOOK_HEIGHT: {
                  int idx = int(_ottavaType) * 2 + (_numbersOnly ? 0 : 12);
                  if (isStyled(Pid::PLACEMENT))
                        return score()->styleI(ss[idx]) == int(Placement::ABOVE) ? Sid::ottavaHookAbove : Sid::ottavaHookBelow;
                  else
                        return placeAbove() ? Sid::ottavaHookAbove : Sid::ottavaHookBelow;
                  }
            default:
                  return TextLineBase::getPropertyStyle(pid);
            }
      }

//---------------------------------------------------------
//   Ottava
//---------------------------------------------------------

Ottava::Ottava(Score* s)
   : TextLineBase(s, ElementFlag::ON_STAFF | ElementFlag::MOVABLE)
      {
      _ottavaType = OttavaType::OTTAVA_8VA;
      setBeginTextPlace(PlaceText::LEFT);
      setContinueTextPlace(PlaceText::LEFT);
      setEndHookType(HookType::HOOK_90);
      setLineVisible(true);
      setBeginHookHeight(Spatium(.0));
      setEndText("");

      initElementStyle(&ottavaStyle);
      }

Ottava::Ottava(const Ottava& o)
   : TextLineBase(o)
      {
      setOttavaType(o._ottavaType);
      _numbersOnly = o._numbersOnly;
      }

//---------------------------------------------------------
//   pitchShift
//---------------------------------------------------------

int Ottava::pitchShift() const
      {
      return ottavaDefault[int(_ottavaType)].shift;
      }

//---------------------------------------------------------
//   createLineSegment
//---------------------------------------------------------

static const ElementStyle ottavaSegmentStyle {
      { Sid::ottavaPosAbove, Pid::OFFSET },
      { Sid::ottavaMinDistance, Pid::MIN_DISTANCE },
      };

LineSegment* Ottava::createLineSegment()
      {
      OttavaSegment* os = new OttavaSegment(this, score());
      os->setTrack(track());
      os->initElementStyle(&ottavaSegmentStyle);
      return os;
      }

//---------------------------------------------------------
//   write
//---------------------------------------------------------

void Ottava::write(XmlWriter& xml) const
      {
      if (!xml.canWrite(this))
            return;
      xml.stag(this);
      writeProperty(xml, Pid::OTTAVA_TYPE);
      writeProperty(xml, Pid::PLACEMENT);
      writeProperty(xml, Pid::NUMBERS_ONLY);
//      for (const StyledProperty& spp : *styledProperties())
//            writeProperty(xml, spp.pid);
      TextLineBase::writeProperties(xml);
      xml.etag();
      }

//---------------------------------------------------------
//   read
//---------------------------------------------------------

void Ottava::read(XmlReader& e)
      {
      eraseSpannerSegments();
      if (score()->mscVersion() < 301)
            e.addSpanner(e.intAttribute("id", -1), this);
      while (e.readNextStartElement())
            readProperties(e);
      if (_ottavaType != OttavaType::OTTAVA_8VA || _numbersOnly != propertyDefault(Pid::NUMBERS_ONLY).toBool())
            styleChanged();
      }

//---------------------------------------------------------
//   readProperties
//---------------------------------------------------------

bool Ottava::readProperties(XmlReader& e)
      {
      const QStringRef& tag(e.name());
      if (tag == "subtype") {
            QString s = e.readElementText();
            bool ok;
            int idx = s.toInt(&ok);
            if (!ok) {
                  _ottavaType = OttavaType::OTTAVA_8VA;
                  for (OttavaDefault d : ottavaDefault) {
                        if (s == d.name) {
                              _ottavaType = d.type;
                              break;
                              }
                        }
                  }
            else if (score()->mscVersion() <= 114) {
                  //subtype are now in a different order...
                  if (idx == 1)
                        idx = 2;
                  else if (idx == 2)
                        idx = 1;
                  _ottavaType = OttavaType(idx);
                  }
            else
                  _ottavaType = OttavaType(idx);
            }
      else  if (readStyledProperty(e, tag))
            return true;
      else if (!TextLineBase::readProperties(e)) {
            e.unknown();
            return false;
            }
      return true;
      }

//---------------------------------------------------------
//   getProperty
//---------------------------------------------------------

QVariant Ottava::getProperty(Pid propertyId) const
      {
      switch (propertyId) {
            case Pid::OTTAVA_TYPE:
                  return int(ottavaType());

            case Pid::NUMBERS_ONLY:
                  return _numbersOnly;

            case Pid::END_TEXT_PLACE:                 // HACK
                  return int(PlaceText::LEFT);

            default:
                  break;
            }
      return TextLineBase::getProperty(propertyId);
      }

//---------------------------------------------------------
//   setProperty
//---------------------------------------------------------

bool Ottava::setProperty(Pid propertyId, const QVariant& val)
      {
      switch (propertyId) {
            case Pid::OTTAVA_TYPE:
                  setOttavaType(OttavaType(val.toInt()));
                  break;

            case Pid::NUMBERS_ONLY:
                  _numbersOnly = val.toBool();
                  break;

            case Pid::SPANNER_TICKS:
                  setTicks(val.value<Fraction>());
                  staff()->updateOttava();
                  break;

            case Pid::SPANNER_TICK:
                  setTick(val.value<Fraction>());
                  staff()->updateOttava();
                  break;

            default:
                  if (!TextLineBase::setProperty(propertyId, val))
                        return false;
                  break;
            }
      triggerLayout();
      return true;
      }

//---------------------------------------------------------
//   propertyDefault
//---------------------------------------------------------

QVariant Ottava::propertyDefault(Pid pid) const
      {
      switch (pid) {
            case Pid::OTTAVA_TYPE:
                  return QVariant();
            case Pid::END_HOOK_TYPE:
                  return int(HookType::HOOK_90);
            case Pid::LINE_VISIBLE:
                  return true;
            case Pid::BEGIN_TEXT_OFFSET:
            case Pid::CONTINUE_TEXT_OFFSET:
            case Pid::END_TEXT_OFFSET:
                  return QPointF();
            case Pid::BEGIN_TEXT_PLACE:
            case Pid::CONTINUE_TEXT_PLACE:
            case Pid::END_TEXT_PLACE:
                  return int(PlaceText::LEFT);
            case Pid::BEGIN_HOOK_TYPE:
                  return int(HookType::NONE);
            case Pid::BEGIN_HOOK_HEIGHT:
                  return Spatium(.0);
            case Pid::END_TEXT:
                  return QString("");
            case Pid::PLACEMENT:
                  return styleValue(Pid::PLACEMENT, getPropertyStyle(Pid::PLACEMENT));

            default:
                  return TextLineBase::propertyDefault(pid);
            }
      }

//---------------------------------------------------------
//   Ottava::propertyId
//---------------------------------------------------------

Pid Ottava::propertyId(const QStringRef& name) const
      {
      if (name == propertyName(Pid::OTTAVA_TYPE))
            return Pid::OTTAVA_TYPE;
      return TextLineBase::propertyId(name);
      }

//---------------------------------------------------------
//   accessibleInfo
//---------------------------------------------------------

QString Ottava::accessibleInfo() const
      {
      return QString("%1: %2").arg(Element::accessibleInfo()).arg(ottavaDefault[static_cast<int>(ottavaType())].name);
      }

//---------------------------------------------------------
//   ottavaTypeName
//---------------------------------------------------------

const char* Ottava::ottavaTypeName(OttavaType type)
      {
      return ottavaDefault[int(type)].name;
      }

}

